import { Color3 } from '@babylonjs/core/Maths/math.color';
import { TransformNode } from '@babylonjs/core/Meshes/transformNode';
import { Helper } from '../../tool/helper';
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
import { AnimationFrame } from '../../animation/animation-frame';
import { Material } from '@babylonjs/core/Materials/material';
import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial';
import { Utils } from '../../tool/utils';
import { IVRContainer } from '../vr-container';
import { IARContainer } from '../ar-container';


/**
 * 提示模式
 * @param SHORT 短亮提示（点击类）
 * @param LONG 长亮提示（按下类）
 */
enum CONTROLLER_TIP_MODE {
    SHORT, LONG
}

/**
 * 按键类型
 * @param TRIGGER 扳机
 * @param SQUEEZE 挤压键
 * @param TOUCHPAD 触摸板
 * @param THUMBSTICK 摇杆
 * @param BUTTON 按钮
 */
enum CONTROLLER_TIP_TYPE {
    TRIGGER, SQUEEZE, TOUCHPAD, THUMBSTICK, BUTTON
};

/**
 * 获取设置的控制器部件字段
 * @param typ 按键类型
 * @returns 部件字段集合
 */
const ControllerGroup = (typ: CONTROLLER_TIP_TYPE): string[] => {
    switch (typ) {
        case CONTROLLER_TIP_TYPE.BUTTON:
            return ['button'];
        case CONTROLLER_TIP_TYPE.SQUEEZE:
            return ['squeeze'];
        case CONTROLLER_TIP_TYPE.TOUCHPAD:
            return ['touchpad'];
        case CONTROLLER_TIP_TYPE.TRIGGER:
            return ['trigger'];
        case CONTROLLER_TIP_TYPE.THUMBSTICK: default:
            return ['thumbstick', 'joystick'];
    }
};


/**
 * 控制器提示
 */
class ControllerTip {

    private static _model: TransformNode[] = [];
    private static _req = {};
    private static _meshes: AbstractMesh[] = [];
    private static _mode: CONTROLLER_TIP_MODE;
    private static _targets: AbstractMesh[] = [];
    private static _duration = 0;
    private static _period = 0;
    private static _count = 0;
    private static _materials: Array<Material | undefined | null> = [];
    private static _replaceMaterial: StandardMaterial;
    private static _typs: CONTROLLER_TIP_TYPE[] = [];
    private static _force = false;

    /**
     * 提示颜色
     */
    public static color: Color3 = new Color3(0, 1, 0);

    /**
     * 短亮周期（帧）
     */
    public static shortPeriod: number = 10;

    /**
     * 长亮周期（帧）
     */
    public static longPeriod: number = 80;

    /**
     * 间隔（帧）
     */
    public static interval: number = 10;

    /**
     * 获取启用状态
     */
    public static get enabled(): boolean {
        return !!this._targets.length;
    }

    /**
     * 初始化，添加事件
     * @param container xr容器
     */
    public static initialize(container: IVRContainer | IARContainer): void {
        const _this = ControllerTip;
        container.onExit(() => {
            const ks = [..._this._typs];
            _this.clearModel();
            _this._typs = [...ks];
        });

        if (!container.xrInput) return;
        container.xrInput.onLoaded((model: TransformNode) => {
            _this.addModel(model);
        });
    }

    /**
     * 清除控制器模型
     */
    public static clearModel(): void {
        this.disable();
        this._model.forEach(t => t.dispose());
        this._model.length = 0;
        this._meshes.length = 0;
    }

    /**
     * 添加控制器模型
     * @param model 控制器模型
     */
    public static addModel(...model: TransformNode[]): void {
        for (const tn of model) {
            if (!tn) continue;
            this._model.push(tn);
            const ms = Helper.getChildMeshes(tn);
            this._meshes.push(...ms);
        }
        if (this._typs.length > 0) {
            this._force = true;
            this.enable(this._mode, ...this._typs);
        }
    }

    /**
     * 启用提示
     * @param mode 提示模式
     * @param keys 目标按键
     */
    public static enable(mode: CONTROLLER_TIP_MODE, ...typs: CONTROLLER_TIP_TYPE[]): void {
        if ((!this._force) && Utils.disorderedArrayEqual(typs, this._typs)) return;
        this._force = false;
        this.disable();
        this._mode = mode;
        this._typs.length = 0;
        for (const t of typs) {
            if (!this._typs.includes(t)) this._typs.push(t);
        }
        if (!this._model.length) return;

        this._targets.length = 0;
        this._materials.length = 0;
        for (const typ of this._typs) {
            const ms = this.getMesh(typ);
            if (!ms) continue;
            ms.forEach(m => {
                this._materials.push(m.material);
                this._targets.push(m);
            });
        }
        if (!this._targets.length) return;
        switch (this._mode) {
            case CONTROLLER_TIP_MODE.SHORT:
                this._period = this.interval + this.shortPeriod;
                this._duration = this.shortPeriod;
                break;
            case CONTROLLER_TIP_MODE.LONG:
                this._period = this.interval + this.longPeriod;
                this._duration = this.longPeriod;
                break;
        }
        this._replaceMaterial = new StandardMaterial('controller-tip-replace-material' + this._targets[0].id, this._targets[0].getScene());
        this._replaceMaterial.diffuseColor = this.color;
        this._replaceMaterial.emissiveColor = new Color3().copyFrom(this.color).scaleInPlace(0.3);
        this._count = 0;
        const _this = ControllerTip;
        AnimationFrame.request(() => {
            _this._count++;
            _this._count %= _this._period;
            for (let i = 0; i < _this._targets.length; i++) {
                const tar = _this._targets[i];
                if (_this._count < _this._duration) {
                    tar.material = _this._replaceMaterial;
                } else {
                    tar.material = _this._materials[i]!;
                }
            }

        }, 16, 200, this._req);
    }

    /**
     * 禁用提示 
     */
    public static disable(): void {
        if (!this.enabled) return;
        AnimationFrame.cancel(this._req);
        for (let i = 0; i < this._targets.length; i++) {
            const tar = this._targets[i];
            tar.material = this._materials[i]!;
        }
        this._targets.length = 0;
        this._materials.length = 0;
        this._typs.length = 0;
        if (this._replaceMaterial) this._replaceMaterial.dispose();
    }

    /**
     * 获取按键对应的网格
     * @param id 按键
     * @returns 按键网格
     */
    private static getMesh(typ: CONTROLLER_TIP_TYPE): AbstractMesh[] | undefined {
        if (!this._model.length) return;
        const ms: AbstractMesh[] = [];
        const ids = ControllerGroup(typ);
        for (const id of ids) {
            const reg = new RegExp(id, 'i');
            for (const m of this._meshes) {
                if (reg.test(m.id)) ms.push(m);
            }
        }
        return ms;
    }

}


export { ControllerTip, CONTROLLER_TIP_MODE, CONTROLLER_TIP_TYPE, ControllerGroup };